﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 支持切换动画的页面导航
    /// </summary>
    public class AnimatedFrame : Frame
    {
        public AnimatedFrame()
        {
            //框架管理自己的历史导航记录（对于Frame嵌套时很重要）
            this.JournalOwnership = System.Windows.Navigation.JournalOwnership.OwnsJournal;
            this.ClipToBounds = true;//防止由于动画对父级的布局产生影响
            this.Loaded += AnimatedFrame_Loaded;
            this.Navigating += AnimatedFrame_Navigating;
            this.Navigated += AnimatedFrame_Navigated;
            this.ContentRendered += AnimatedFrame_ContentRendered;
        }
        private ContentPresenter ContentParent;
        private Image AniImage;

        private void AnimatedFrame_Loaded(object sender, RoutedEventArgs e)
        {
            this.ContentParent = GetThisContentParent();
            Grid grid = new Grid();
            AniImage = new Image() { VerticalAlignment = VerticalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch };
            if (this.ContentParent.Parent is Panel p)
            {
                p.Children.Remove(this.ContentParent);
                p.Children.Add(grid);
            }
            else if (this.ContentParent.Parent is Border b)
            {//没有导航栏
                b.Child = grid;
            }
            else
            {
                throw new Exception("意外的FrameUI层次。");
            }
            grid.Children.Add(this.ContentParent);
            grid.Children.Add(this.AniImage);


            this.ContentParent.RenderTransformOrigin = new Point(0.5, 0.5);
            this.AniImage.RenderTransformOrigin = new Point(0.5, 0.5);
        }
        /// <summary>
        /// 查找frame的ContentPresenter
        /// </summary>
        /// <returns></returns>
        private ContentPresenter GetThisContentParent()
        {
            //var border = VisualTreeHelper.GetChild(this, 0);
            //var docker = VisualTreeHelper.GetChild(border, 0);
            //if (docker is ContentPresenter cp)
            //{//导航栏隐藏时
            //    return cp;
            //}
            //var child1 = VisualTreeHelper.GetChild(docker, 1);
            //if (child1 is ContentPresenter)
            //{
            //    cp = (ContentPresenter)child1;
            //}
            //else
            //{
            //    cp = (ContentPresenter)VisualTreeHelper.GetChild(docker, 0);
            //}
            var cp = this.FindChid<ContentPresenter>(this);
            return cp;
        }

        private T FindChid<T>(DependencyObject ui) where T : DependencyObject
        {
            var count = VisualTreeHelper.GetChildrenCount(ui);
            List<DependencyObject> temp = new List<DependencyObject>(count);
            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(ui, i);
                if (child is T t)
                {
                    return t;
                }
                temp.Add(child);
            }
            foreach (var item in temp)
            {
                var founded = FindChid<T>(item);
                if (founded != null)
                {
                    return founded;
                }
            }
            return null;
        }

        private void AnimatedFrame_ContentRendered(object sender, EventArgs e)
        {
        }
        private Random random = new Random();
        private void AnimatedFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {

        }
        RenderTargetBitmap curViewImg;
        private void AnimatedFrame_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
        {//开始导航时缓存当前画面

            if (this.ContentParent != null)
            {
                var content = VisualTreeHelper.GetChild(this.ContentParent, 0);

                if (this.curViewImg != null)
                {
                    this.curViewImg = null;
                    this.AniImage.Source = null;
                }

                this.curViewImg = new RenderTargetBitmap((int)this.ContentParent.ActualWidth, (int)this.ContentParent.ActualHeight, 96, 96, PixelFormats.Pbgra32);
                this.curViewImg.Clear();
                this.curViewImg.Render(this.ContentParent);
                //if (this.AniImage.Source != null)
                //{
                //    GC.Collect();
                //}
                this.AniImage.BeginInit();
                this.AniImage.Source = this.curViewImg;
                this.AniImage.EndInit();

            }
            if (this.ContentParent != null)
            {
                if (WidthAnimation)
                {
                    if (RandomAnimation)
                    {
                        var types = Enum.GetValues(typeof(AnimationType));
                        var index = random.Next(types.Length);
                        this.AnimationType = (AnimationType)types.GetValue(index);
                    }
                    this.PlayAni();
                }

            }
        }
        /// <summary>
        /// 播放切换动画
        /// </summary>
        private void PlayAni()
        {
            this.AniImage.Visibility = Visibility.Visible;//开始动画前显示缓存图片
            Storyboard storyboard = new Storyboard() { FillBehavior = FillBehavior.Stop };
            this.CreateOutAni(storyboard);
            this.CreateInAni(storyboard);
            storyboard.Completed += Storyboard_Completed;
            storyboard.Begin();
        }

        private void Storyboard_Completed(object sender, EventArgs e)
        {
            this.ContentParent.RenderTransformOrigin = new Point(0.5, 0.5);
            this.AniImage.RenderTransformOrigin = new Point(0.5, 0.5);
            this.ContentParent.RenderTransform = null;
            this.AniImage.RenderTransform = null;
            this.AniImage.Visibility = Visibility.Collapsed;//动画完成后隐藏图片
            //   GC.Collect();
        }

        /// <summary>
        /// 创建出场动画
        /// </summary>
        private void CreateOutAni(Storyboard storyboard)
        {
            this.AniImage.RenderTransformOrigin = new Point(0.5, 0.5);
            this.AniImage.RenderTransform = null;
            if (this.AnimationType == AnimationType.Fade)
            {//渐隐
                DoubleAnimation doubleAnimation = new DoubleAnimation(1, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(Image.OpacityProperty.Name));
                storyboard.Children.Add(doubleAnimation);
            }
            else if (this.AnimationType == AnimationType.Spin)
            {//旋转
                TransformGroup transg = new TransformGroup();
                RotateTransform rotateTransform = new RotateTransform();
                ScaleTransform scaleTransform = new ScaleTransform();
                transg.Children.Add(rotateTransform);
                transg.Children.Add(scaleTransform);
                this.AniImage.RenderTransform = transg;

                DoubleAnimation doubleAnimation = new DoubleAnimation(1, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(Image.OpacityProperty.Name));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(0, this.AnimationDirection == FlowDirection.LeftToRight ? -360 : 360, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(1, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(1, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"));
                storyboard.Children.Add(doubleAnimation);
            }
            else if (this.AnimationType == AnimationType.Slide)
            {//平移
                TranslateTransform translateTransform = new TranslateTransform();
                this.AniImage.RenderTransform = translateTransform;
                var fromX = 0;
                var toX = -Math.Max(this.AniImage.ActualWidth, this.ContentParent.ActualWidth);
                if (this.AnimationDirection == FlowDirection.LeftToRight)
                {//从左往右出场
                    toX = Math.Max(this.AniImage.ActualWidth, this.ContentParent.ActualWidth);
                }
                var doubleAnimation = new DoubleAnimation(fromX, toX, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TranslateTransform.X)"));
                storyboard.Children.Add(doubleAnimation);

            }
            else if (this.AnimationType == AnimationType.Grow)
            {//伸展 
                this.AniImage.RenderTransformOrigin = new Point(0, 0.5);
                if (this.AnimationDirection == FlowDirection.LeftToRight)
                {//从左往右出场
                    this.AniImage.RenderTransformOrigin = new Point(1, 0.5);
                }
                ScaleTransform scaleTransform = new ScaleTransform();
                this.AniImage.RenderTransform = scaleTransform;
                var doubleAnimation = new DoubleAnimation(1, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(ScaleTransform.ScaleX)"));
                storyboard.Children.Add(doubleAnimation);

            }
            else if (this.AnimationType == AnimationType.Skew)
            {//斜切
                this.AniImage.RenderTransformOrigin = new Point(0, 0);
                SkewTransform skewTransform = new SkewTransform();
                this.AniImage.RenderTransform = skewTransform;
                var doubleAnimation = new DoubleAnimation(0, this.AnimationDirection == FlowDirection.LeftToRight ? -90 : 90, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.AniImage);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(SkewTransform.AngleX)"));
                storyboard.Children.Add(doubleAnimation);
            }
        }
        /// <summary>
        /// 创建入场动画
        /// </summary>
        /// <param name="storyboard"></param>
        private void CreateInAni(Storyboard storyboard)
        {
            this.ContentParent.RenderTransformOrigin = new Point(0.5, 0.5);
            this.ContentParent.RenderTransform = null;
            if (this.AnimationType == AnimationType.Fade)
            { //渐显
                DoubleAnimation doubleAnimation = new DoubleAnimation(0, 1, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(ContentPresenter.OpacityProperty.Name));
                storyboard.Children.Add(doubleAnimation);

            }
            else if (this.AnimationType == AnimationType.Spin)
            {//旋转

                TransformGroup transg = new TransformGroup();
                RotateTransform rotateTransform = new RotateTransform();
                ScaleTransform scaleTransform = new ScaleTransform();
                transg.Children.Add(rotateTransform);
                transg.Children.Add(scaleTransform);
                this.ContentParent.RenderTransform = transg;

                DoubleAnimation doubleAnimation = new DoubleAnimation(0, 1, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(ContentPresenter.OpacityProperty.Name));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(0, this.AnimationDirection == FlowDirection.LeftToRight ? 360 : -360, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(0, 1, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"));
                storyboard.Children.Add(doubleAnimation);

                doubleAnimation = new DoubleAnimation(0, 1, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"));
                storyboard.Children.Add(doubleAnimation);
            }
            else if (this.AnimationType == AnimationType.Slide)
            {//平移
                TranslateTransform translateTransform = new TranslateTransform();
                this.ContentParent.RenderTransform = translateTransform;
                var fromX = this.ContentParent.ActualWidth;
                var toX = 0;
                if (this.AnimationDirection == FlowDirection.LeftToRight)
                {//从左往右入场
                    fromX = -this.ContentParent.ActualWidth;
                }
                var doubleAnimation = new DoubleAnimation(fromX, toX, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(TranslateTransform.X)"));
                storyboard.Children.Add(doubleAnimation);

            }
            else if (this.AnimationType == AnimationType.Grow)
            {//伸展
                this.ContentParent.RenderTransformOrigin = new Point(1, 0.5);
                if (this.AnimationDirection == FlowDirection.LeftToRight)
                {//从左往右入场
                    this.ContentParent.RenderTransformOrigin = new Point(0, 0.5);
                }
                ScaleTransform scaleTransform = new ScaleTransform();
                this.ContentParent.RenderTransform = scaleTransform;
                var doubleAnimation = new DoubleAnimation(0, 1, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(ScaleTransform.ScaleX)"));
                storyboard.Children.Add(doubleAnimation);

            }
            else if (this.AnimationType == AnimationType.Skew)
            {//斜切
                this.ContentParent.RenderTransformOrigin = new Point(0.5, 0.5);
                SkewTransform skewTransform = new SkewTransform();
                this.ContentParent.RenderTransform = skewTransform;
                var doubleAnimation = new DoubleAnimation(this.AnimationDirection == FlowDirection.LeftToRight ? 90 : -90, 0, new Duration(this.AnimationTime));
                Storyboard.SetTarget(doubleAnimation, this.ContentParent);
                Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(RenderTransform).(SkewTransform.AngleX)"));
                storyboard.Children.Add(doubleAnimation);
            }
        }
        /// <summary>
        /// 动画时长
        /// </summary> 
        public TimeSpan AnimationTime
        {
            get { return (TimeSpan)GetValue(AnimationTimeProperty); }
            set { SetValue(AnimationTimeProperty, value); }
        }

        public static readonly DependencyProperty AnimationTimeProperty =
            DependencyProperty.Register("AnimationTime", typeof(TimeSpan), typeof(AnimatedFrame), new PropertyMetadata(TimeSpan.FromSeconds(1.1)));


        /// <summary>
        /// 动画类型
        /// </summary> 
        public AnimationType AnimationType
        {
            get { return (AnimationType)GetValue(AnimationTypeProperty); }
            set { SetValue(AnimationTypeProperty, value); }
        }

        public static readonly DependencyProperty AnimationTypeProperty =
            DependencyProperty.Register("AnimationType", typeof(AnimationType), typeof(AnimatedFrame), new PropertyMetadata(AnimationType.Fade));


        /// <summary>
        /// 切换时是否播放动画
        /// </summary> 
        public bool WidthAnimation
        {
            get { return (bool)GetValue(WidthAnimationProperty); }
            set { SetValue(WidthAnimationProperty, value); }
        }

        public static readonly DependencyProperty WidthAnimationProperty =
            DependencyProperty.Register("WidthAnimation", typeof(bool), typeof(AnimatedFrame), new PropertyMetadata(true));



        /// <summary>
        /// 是否播放随机动画，若为True，手动设置AnimationType则无效
        /// </summary> 
        public bool RandomAnimation
        {
            get { return (bool)GetValue(RandomAnimationProperty); }
            set { SetValue(RandomAnimationProperty, value); }
        }

        public static readonly DependencyProperty RandomAnimationProperty =
            DependencyProperty.Register("RandomAnimation", typeof(bool), typeof(AnimatedFrame), new PropertyMetadata(true));


        /// <summary>
        /// 动画方向
        /// </summary>
        public FlowDirection AnimationDirection
        {
            get { return (FlowDirection)GetValue(AnimationDirectionProperty); }
            set { SetValue(AnimationDirectionProperty, value); }
        }

        public static readonly DependencyProperty AnimationDirectionProperty =
            DependencyProperty.Register("AnimationDirection", typeof(FlowDirection), typeof(AnimatedFrame), new PropertyMetadata(FlowDirection.RightToLeft));


    }
    /// <summary>
    /// 动画类型
    /// </summary>
    public enum AnimationType
    {
        /// <summary>
        /// 渐隐渐显
        /// </summary>
        Fade,
        /// <summary>
        /// 滑动
        /// </summary>
        Slide,
        /// <summary>
        /// 伸展
        /// </summary>
        Grow,
        /// <summary>
        /// 翻转
        /// </summary>
        Skew,
        /// <summary>
        /// 旋转
        /// </summary>
        Spin
    }
}
